package com.zxy.zxyrpc.loadBalancer;

import cn.hutool.core.lang.hash.Hash32;
import cn.hutool.core.util.HashUtil;
import com.zxy.zxyrpc.model.ServiceMetaInfo;

import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * 一致性哈希负载器
 *      todo 存在线程安全问题,采用 synchronized 解决
 *
 * @author zxy
 * @since 2025/5/27
 **/
public class ConsistenHashLoadBalancer implements LoadBalancer {
    // 每个节点所持有的虚拟节点数（感觉过于庞大）
    private static final int VIRTUAL_NODE_NUM = 10;
    // 一致性 hash 环，存放虚拟节点
    private final TreeMap<Integer, ServiceMetaInfo> virtualNodes = new TreeMap<>();

    @Override
    public ServiceMetaInfo select(Map<String, Object> reqParams, List<ServiceMetaInfo> serviceMetaInfoList) {
        // 为空则为空，只有一个就无需哈希
        if (serviceMetaInfoList.isEmpty())
            return null;
        if (1 == serviceMetaInfoList.size())
            return serviceMetaInfoList.get(0);
        // 构建虚拟节点环
        Map.Entry<Integer, ServiceMetaInfo> entry;
        synchronized (ConsistenHashLoadBalancer.class){
            for (ServiceMetaInfo serviceMetaInfo : serviceMetaInfoList) {
                for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
                    int hash = getHash(serviceMetaInfo.getServiceAddress() + "#" + i);
                    virtualNodes.put(hash, serviceMetaInfo);
                }
            }
            // 请求对象的hash值
            int hash = getHash(reqParams);
            // 选择最近且大于等于调用请求 hash 值的虚拟节点
            entry = virtualNodes.ceilingEntry(hash);
            // 如果没有大于等于调用请求 hash 值的虚拟节点，则返回环首部的节点(因为不是真的环，所以极端情况下要倒转)
            if (entry == null)
                entry = virtualNodes.firstEntry();
        }
        return entry.getValue();
    }

    /**
     * hash 算法，可自行实现(MurmurHash 或 FNV-1a)
     *
     * @param key 要hash的对象
     * @return hash值
     */
    private int getHash(Object key) {
        return key.hashCode();
    }
}
// ConcurrentSkipListMap。put,get 操作线程安全案例
/*public class ConsistenHashLoadBalancer implements LoadBalancer {
    private final Hash32<Object> hashFunc = (key) -> HashUtil.fnvHash(key.toString());
    private final int numberOfReplicas = 10;
    private final SortedMap<Integer, ServiceMetaInfo> circle = new ConcurrentSkipListMap<>();

    @Override
    public ServiceMetaInfo select(Map<String, Object> requestParams, List<ServiceMetaInfo> serviceMetaInfoList) {
        circle.clear();
        for (ServiceMetaInfo serviceMetaInfo : serviceMetaInfoList) {
            add(serviceMetaInfo);
        }
        return get(requestParams);
    }

    private void add(ServiceMetaInfo serviceMetaInfo) {
        for (int i = 0; i < this.numberOfReplicas; i++) {
            this.circle.put(this.hashFunc.hash32(serviceMetaInfo.getServiceAddress() + "#" + i), serviceMetaInfo);
        }
    }

    private ServiceMetaInfo get(Object key) {
        int hash = this.hashFunc.hash32(key);
        if (!this.circle.containsKey(hash)) {
            SortedMap<Integer, ServiceMetaInfo> tailMap = this.circle.tailMap(hash);
            // 没有测回溯到首节点
            hash = tailMap.isEmpty() ? this.circle.firstKey() : tailMap.firstKey();
        }
        return this.circle.get(hash);
    }
}*/

